﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using BrightIdeasSoftware;
using ROrganizer.UI.Controllers;
using ROrganizer.UI.Forms.Core;
using ROrganizer.UI.Models;

namespace ROrganizer.UI.Forms
{
    internal partial class ROrganizerForm : Form
    {
        private readonly ROrganizerFormController controller;
        private readonly GroupManager<ROrganizerFormController.Entry, ROrganizerFormController.GroupMode?> groupper;

        public ROrganizerForm()
        {
            this.controller = new ROrganizerFormController();
            this.InitializeComponent();

            this.groupper = new GroupManager<ROrganizerFormController.Entry, ROrganizerFormController.GroupMode?>(this.olvEntries, this.controller.GroupBy);
            this.InitializeGroups();

            this.chTitle.FreeSpaceProportion = 5;
            this.chGenre.FreeSpaceProportion = 2;
            this.chKeywords.FreeSpaceProportion = 2;

            this.olvEntries.UseFiltering = true;
            this.olvEntries.ModelFilter = this.controller;

            this.gbByTitle.Checked = true;
            this.olvEntries.AlwaysGroupByColumn = this.chTitle;
            this.controller.GroupBy(ROrganizerFormController.GroupMode.ByTitle);

            this.olvEntries.Objects = this.controller.Entries;
            this.olvEntries.Sort(this.chTitle, SortOrder.Ascending);

            this.controller.Entries.CollectionChanged += this.OnChanged;
            this.controller.Genres.CollectionChanged += this.OnGenresChanged;
            this.tsbManageProposals.Visible = ViewSettings.IsExtendedModeEnabled;

            foreach (var genre in this.controller.Genres)
                this.tsddbGenres.DropDownItems.Add(this.BuildGenreItem(genre));
        }

        private void InitializeGroups()
        {
            this.groupper.Bind(this.gbByTitle, this.chTitle, ROrganizerFormController.GroupMode.ByTitle, SortOrder.Ascending);
            this.groupper.SetKey(this.chTitle, x => x.Title.ToCharArray().Select(c => Char.ToUpper(c)).First(c => Char.IsLetterOrDigit(c)));

            this.groupper.Bind(this.gbByRating, this.chRating, ROrganizerFormController.GroupMode.ByRating, SortOrder.Descending);
            this.groupper.SetKey(this.chRating, x => Formatter.GetRatingGroup(x.Rating));
            this.groupper.SetFormat<short?>(this.chRating, Formatter.FormatRating);
            this.groupper.SetTitle<short?>(this.chRating, Formatter.FormatRatingGroup);

            this.groupper.Bind(this.gbByPublicationDate, this.chPublished, ROrganizerFormController.GroupMode.ByPublicationDate, SortOrder.Descending);
            this.groupper.SetKey(this.chPublished, x => x.Published.HasValue ? x.Published.Value.Year : default(int?));
            this.groupper.SetTitle<int?>(this.chPublished, Formatter.FormatPublishedGroup);

            this.groupper.Bind(this.gbByQuality, this.chResolution, ROrganizerFormController.GroupMode.ByQuality, SortOrder.Descending);
            this.groupper.SetKey(this.chResolution, x => Formatter.GetResolutionGroup(x.Resolution));
            this.groupper.SetFormat<Resolution>(this.chResolution, Formatter.FormatResolution);
            this.groupper.SetFormat<int?>(this.chResolution, Formatter.FormatResolutionGroup);

            this.groupper.Bind(this.gbBySize, this.chSize, ROrganizerFormController.GroupMode.BySize, SortOrder.Ascending);
            this.groupper.SetKey(this.chSize, x => Formatter.GetSizeGroup(x.Size));
            this.groupper.SetFormat<long>(this.chSize, Formatter.FormatSize);
            this.groupper.SetTitle<long>(this.chSize, Formatter.FormatSizeGroup);

            this.groupper.Bind(this.gbByKeyword, this.chKeywords, ROrganizerFormController.GroupMode.ByKeword, SortOrder.Ascending);
            this.groupper.SetKey(this.chKeywords, x => x.Keywords.ToCharArray().Select(c => Char.ToUpper(c)).First(c => Char.IsLetterOrDigit(c)));

            this.groupper.Bind(this.gbByGenre, this.chGenre, ROrganizerFormController.GroupMode.ByGenre, SortOrder.Ascending);
            this.groupper.SetFormat<int>(this.chRuntime, Formatter.FormatRuntime);
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            this.UpdateTotals();
            this.olvEntries.Normalize();
        }

        private ToolStripMenuItem BuildGenreItem(string genre)
        {
            var item = new ToolStripMenuItem 
            { 
                Text = genre, 
                Name = genre, 
                Tag = genre,
                CheckOnClick = true, 
                Checked = this.controller.IsGenreChoosen(genre) 
            };

            item.Click += this.OnGenreClicked;
            return item;
        }

        private void OnChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (this.SynchronizeInvocation(this.OnChanged, sender, e))
            {
                if (e.Action == NotifyCollectionChangedAction.Reset)
                    this.olvEntries.ClearObjects();

                if (e.OldItems != null)
                    this.olvEntries.RemoveObjects(e.OldItems);

                if (e.NewItems != null)
                    this.olvEntries.AddObjects(e.NewItems);

                this.UpdateTotals();
            }
        }

        private void OnGenresChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (this.SynchronizeInvocation(this.OnGenresChanged, sender, e))
            {
                if (e.Action == NotifyCollectionChangedAction.Reset)
                    while (this.tsddbGenres.DropDownItems.Count > 3)
                        this.tsddbGenres.DropDownItems.RemoveAt(3);

                if (e.OldItems != null)
                    foreach (string item in e.OldItems)
                        this.tsddbGenres.DropDownItems.RemoveByKey(item);

                if (e.NewItems != null)
                    foreach (string genre in e.NewItems)
                        this.tsddbGenres.DropDownItems.Add(this.BuildGenreItem(genre));
            }
        }

        private void OnGenreClicked(object sender, EventArgs e)
        {
            var item = (ToolStripMenuItem)sender;
            var genre = item.Name;

            this.controller.SetGenreFilter(genre, item.Checked);
            this.olvEntries.BuildList(true);
        }

        private void OnFilterChanged(object sender, EventArgs e)
        {
            this.controller.Filter = this.tstFilter.Text;
            this.olvEntries.BuildList(true);
        }

        private void OnManageLibrary(object sender, EventArgs e)
        {
            this.olvEntries.BeginUpdate();
            this.ExecuteAndWatch(this.controller.ShowLibraryManager, this);
            this.olvEntries.EndUpdate();
        }

        private void OnManageProposals(object sender, EventArgs e)
        {
            this.ExecuteAndWatch(this.controller.ShowProposalsManager, this);
        }

        private void OnDoubleClicked(object sender, MouseEventArgs e)
        {
            if (this.olvEntries.SelectedObject == null)
                return;

            this.ExecuteAndWatch(this.controller.ShowDetails, this, this.olvEntries.SelectedObject as ROrganizerFormController.Entry);
        }

        private void OnShowInfo(object sender, EventArgs e)
        {
            this.ExecuteAndWatch(this.controller.ShowDetails, this, this.olvEntries.SelectedObject as ROrganizerFormController.Entry);
        }

        private void OnUpdate(object sender, EventArgs e)
        {
            this.olvEntries.BeginUpdate();
            this.ExecuteAndWatch(this.controller.ShowUpdate, this);
            this.olvEntries.EndUpdate();
        }

        private void OnAbout(object sender, EventArgs e)
        {
            this.ExecuteAndWatch(this.controller.ShowAbout, this);
        }

        private void OnShowAllGenres(object sender, EventArgs e)
        {
            foreach (var item in this.tsddbGenres.DropDownItems.OfType<ToolStripMenuItem>())
            {
                if (item.Tag != null && item.Checked == false)
                {
                    item.Checked = true;
                    this.controller.SetGenreFilter(item.Name, true);
                }
            }

            this.olvEntries.BuildList(true);
        }

        private void OnHideAllGenres(object sender, EventArgs e)
        {
            foreach (var item in this.tsddbGenres.DropDownItems.OfType<ToolStripMenuItem>())
            {
                if (item.Tag != null && item.Checked == true)
                {
                    item.Checked = false;
                    this.controller.SetGenreFilter(item.Name, false);
                }
            }

            this.olvEntries.BuildList(true);
        }

        private void OnCopyTo(object sender, EventArgs e)
        {
            this.ExecuteAndWatch(this.controller.CopyTo, this, this.GetSelectedEntries());
        }

        private void OnRemove(object sender, EventArgs e)
        {
            this.ExecuteAndWatch(this.controller.Remove, this, this.GetSelectedEntries());
        }

        private void UpdateTotals()
        {
            this.sslRuntime.Text = "Runtime: " + Formatter.FormatRuntime(this.controller.TotalRuntime);
            this.sslMovies.Text = "Movies: " + this.controller.TotalMovies;
            this.sslSize.Text = "Size: " + Formatter.FormatSize(this.controller.TotalSize);
        }

        private ROrganizerFormController.Entry[] GetSelectedEntries()
        {
            return this.olvEntries.SelectedObjects.OfType<ROrganizerFormController.Entry>().ToArray();
        }
    }
}
